/**
 *  Model with a low level of fear
 *  Author: tom
 *  Description: 
 */

model EvacuationWithFire
import "Common.gaml"

global{
	
	int percent_of_fear <- 30 min: 0 max: 100;
	int nb_people <- 100 min: 1 max: 2000;
	int total_exit_time <- 0;
	int simulation_time <- 0;
	int adjustment_factor <- 20;
	bool follow_other <- true;
	int nb_exit <- 0;
	
	float max_fear_level <- 0.9;
	float max_human_size <- 2.0;
	int minimal_distance <- 2;
	int total_collision <- 0;
	list ports of: port;
	list obstacles of: obstacle;
	list obs of: geometry;
	geometry port3_visible1;
	graph the_graph;
	
	////defination for propagation////////	
	bool apply_propagation <- true;
	float max_contagion_distance <- 20.0;
	////end defination for propagation////

	////defination for decay of emotion////////	
	bool apply_decay <- true;
	float decay_factor <- 0.005;
	////end defination for decay of emotion////

	////fire stuff////////////////////
	int nb_fire <- 5;
	float fire_fear_contribute_factor <- 0.01;
	init {
		create obstacle from : shape_file_obstacles returns: list_of_obstacle ;
		create background from : shape_file_bounds returns: list_of_bg ;
		create port from : shape_file_ports ;
		
		set port3_visible1 <- polygon([{16,117}, {16, 97}, {181, 97}, {181, 117}]);
		
		set obstacles <- obstacles union list ( list_of_obstacle ) ;
		loop ob over: obstacles{			
//			ask ob{				
//				set obs <- obs union list (shape + 2.0);
//			}
			let geo <- ob + 2.0;
			set obs <- obs union list (geo);
			//set ob. <- ob + 2.0;
		}
		
		set bg <- first ( list_of_bg ) ;
		
		ask target : bg {
			set shape <- self - obstacles ;
		}

		set ports <- list ( port ) ;
		
		//initial people
		create people number : nb_people {
			set location <- any_location_in ( bg ) ;
			if(flip(percent_of_fear/100.0)){
				set fear_level <- max_fear_level;
				//normal state				
				if(fear_level >= panic_threshold){
					set speed <- panic_speed;
				}else if (fear_level >= average_fear_threshold){
					set speed <- average_fear_speed;
				}else if (fear_level >= little_fear_threshold){
					set speed <- little_fear_speed;				
				}else{
					set speed <- normal_speed;
				}								
			}else{
				
				set speed <- normal_speed;
			}
			
			//set target if agent is not panic
			do find_target;
			
			set adj_factor <- adjustment_factor;
			set epsilon <- rnd(70)/100 + 0.3;
			set delta <- rnd(70)/100 + 0.3;
			//set the_target <- first ( ports ) ;
		}
		
		//initial fire
		create fire number : nb_fire {
			let possible_location <- first(list_of_bg);
			//bound - max fire size
			let ob <- one_of(obstacles);
			set location <- any_location_in ( ob );
		}

	}
}

entities {
	species people skills: [moving, situated, visible] control:fsm{
		float size <- max_human_size;
		rgb color <- rgb('yellow');
		float fear_level <- 0.0;
		point velocity <- {0,0};
		int adj_factor <- adjustment_factor;
		
		int perception_range <- 30;
		geometry tar <- nil;
		people guider <- nil;
		point prev_location <- nil;
		int stuck <- -1000;
		
		bool avoid_obstacle_fire <- false;
		
		float epsilon <- 0.0 min: 0.0 max: 1.0;
		float delta <- 0.0 min: 0.0 max: 1.0;
				
		aspect base {
			draw shape : circle size : size color : color ;
		}
		
		reflex set_state{
			if(fear_level >= panic_threshold){
				if(state != 'panic'){
					set state <- 'panic';
				}
			}else if (fear_level >= average_fear_threshold){
				if(state != 'average_fear'){
					set state <- 'average_fear';				
				}
			}else if (fear_level >= little_fear_threshold){
				if(state != 'little_fear'){
					set state <- 'little_fear';
				}
			}else {
				if(state != 'normal'){
					set state <- 'normal';
				}
			}			
		}
				
		reflex separation{
			let acc value: {0,0};
			loop pp over: (people overlapping (circle(minimal_distance*1.5)))  {
				set acc <- acc - ((location of pp) - my(location));
			}
			if(state = 'panic'){
				set velocity <- velocity + acc;
			}else{
				set velocity <- velocity + acc + acc;			
			}
		}
		reflex avoid_obstacle{
			let acc <- {0,0};
			let nearby_obstacles <- (obstacle overlapping (circle (minimal_distance)) );
			if(nearby_obstacles != nil){
				loop obs over: nearby_obstacles {
					set acc <- acc - ((location of (obs inter circle (minimal_distance * 3.5))) - my (location));
//					let inte <- circle (minimal_distance) inter as_egde_graph(obs);
//					let p1 <- any_point_in(inte);
//					let p2 <- any_point_in(inte);
//write("p1:"+ p1.x + ", " + p1.y);
//write("p2:"+ p2.x + ", " + p2.y);					
					
					set acc <- acc - (location of (obs) - my (location));
				}
			}
			set velocity <- velocity + acc/2; 
		}

		reflex avoid_fire{
			let acc <- {0,0};
			let nearby_fires <- (fire overlapping (circle (minimal_distance*3)) );
			if(nearby_fires != nil){
				loop fi over: nearby_fires {
					if(fi overlaps bg){
						set acc <- acc - ((location of fi) - my(location));					
					}
				}
				if(length(nearby_fires) > 2){
					set avoid_obstacle_fire <- true;
				}
				//increase fear_level				
			}else{
				set avoid_obstacle_fire <- false;
			}
			set velocity <- velocity + acc;
		}
		
		reflex percept {
			let percept_fires <- (fire overlapping (circle (perception_range)) );
			//do increase_fear_level nb_fires_seen: length(percept_fires);
			if(percept_fires != nil){
				loop fi over: percept_fires{
					set fear_level <- fear_level + (perception_range - (self distance_to fi))/perception_range*fire_fear_contribute_factor;
				}
			}			
			if(fear_level > 1.0){
				set fear_level <- 1.0;
			}
			
		}
		reflex to_target when: tar != nil{
			if(avoid_obstacle_fire){
				set velocity <- velocity + (((location of tar) - location)/adj_factor/1);
				set avoid_obstacle_fire <- false;
			}else{
				set velocity <- velocity + (((location of tar) - location)/adj_factor);
			}
			
		}

		reflex normal_move when: state != 'panic'{
			do do_move;
		}
		
		/**
		 * quand les agents n'ont pas peur, ils déplacent lentement ou ne déplacent pas 
		 */
		reflex on_target when: tar != nil{
			if(self distance_to tar < size){
				//set tar <- nil;
				set total_exit_time <- total_exit_time + cycle;
				set nb_exit <- nb_exit + 1;
				set simulation_time <- cycle;
				if(nb_exit = nb_people){					
					do write_to_console;
				}				
				do die;
			}
		}
		
		reflex adjust when: circle(30) overlaps tar{						
			set adj_factor <- 4;
		}
				
		reflex random_run when: state = 'panic'{			
			let ps <- first(ports sort_by(self distance_to each));			
			//if (circle(max_human_size) overlaps port3_visible1){
			if (self distance_to ps < perception_range){
				set tar <- ps;
				do do_move;
				set guider <- nil;		
			}else{
				//follow
				if(follow_other){
					let closetPeople <- first(self neighbours_at 5 of_species people where (each.state != 'panic') sort_by (self distance_to each));
					if(closetPeople != nil ){
						set speed <- closetPeople.speed;
						set guider <- closetPeople;
						set heading <- closetPeople.heading;
						do do_panic_move;
					}else{
						set speed <- panic_speed;
						do wander speed: speed bounds:bg;
						set guider <- nil;	
					}										
				}else{
					set speed <- panic_speed;
					do wander speed: speed bounds:bg;
				}
			}
			
		}
		
		reflex change_target when: state != 'panic'{
//			if(circle(speed/3) overlaps prev_location){
//write("target changed");								
//				let ps <- ports sort_by(self distance_to each);
//				let found <- false;
//				loop p over: ps{
//					if(p != tar and not found){
//						set tar <- p;
//						set found <- true;
//					}
//				}								
//			}
		}
//		reflex find_target{
//			//set port <- sort_by (self distance_to each);			
//			let ps <- ports sort_by(self distance_to each);			
//			set tar <- first(ps);
//			
//		}
		reflex do_wander when: tar = nil{
			do wander speed: speed bounds: bg;						
		}
		
		reflex be_contaminate when: apply_propagation{
			let candidates <- self neighbours_at max_contagion_distance of_species people where (each.fear_level > fear_level);
			if(candidates != nil){
				let max_transmition_fear <- 0.0;
				loop candidate over: candidates{
					let dist <- self distance_to candidate; 
					let i <- candidate.fear_level * candidate.epsilon * delta*(max_contagion_distance - dist)/max_contagion_distance;
					if(max_transmition_fear < i){
						set max_transmition_fear <- i;
					}
				}
				if(max_transmition_fear > fear_level){
					set fear_level <- max_transmition_fear;		 
				}
			}
		}
		
		reflex decay when: apply_decay and fear_level > 0{
			set fear_level <- fear_level * (1 - decay_factor);			
		}

		reflex collision when: cycle > 1{
			//let collision type: list of: people <- (self );
			
			let distance_collision <- 0.95 * max_human_size;
			let closes type: list of: people <-  (self neighbours_at distance_collision of_species people);
			set total_collision <- total_collision + length(closes);
		}
		
		action increase_fear_level{
			arg nb_fires_seen type : int ;
			set fear_level <- fear_level + nb_fires_seen*fire_fear_contribute_factor;
			if(fear_level > 1.0){
				set fear_level <- 1.0;
			}
		}
				
		action do_move{
			if (abs(velocity.x) + abs(velocity.y) < 2) {
				set velocity <- {(rnd(5)) -2, (rnd(5)) - 2};
			}
			set prev_location <- location;
//			if(velocity.x + velocity.y < speed/2){
//				set velocity <- velocity*2;
//			}
			do goto target: location + velocity;
			set velocity <- location - prev_location;			
		}		
				
		action do_panic_move{
			do wander speed: speed amplitude: 60 bounds:bg;			
		}		

		action find_target{
			//set target if agent is not panic
			if(fear_level < panic_threshold){
				let ps <- ports sort_by(self distance_to each);			
				set tar <- first(ps);				
			}			
		}
		
		action write_to_console{
			write(""+ total_collision + "," +  simulation_time + "," +  total_exit_time/nb_exit);		
		}				

		//the states
		state 'normal' initial: true{
			set color <- rgb('yellow');	
			set speed <- normal_speed;
			do find_target;
		}
		
		state 'little_fear'{
			set color <- rgb([ 250 , rnd (220) , 10 ]);
			set speed <- little_fear_speed;
			do find_target;
		}
		
		state 'average_fear'{
			set color <- rgb([ 250 , rnd (150) , 10 ]);
			set speed <- average_fear_speed;
			do find_target;
		}
				
		state 'panic'{
			set color <- rgb([ 250 , rnd (50) , 10 ]);
			set speed <- panic_speed;
			//set tar <- nil;
		}		 	
	}

}

experiment Evacuation type: gui {
	//parameter 'Vitesse des agents m/s:' var: mouvement_speed category: 'Model' ;
	parameter 'Niveau de peur:' var: max_fear_level category: 'Model' ;
	parameter 'Pourcentage d\'agent ayant peur :' var: percent_of_fear category: 'Model' ;
	parameter 'Suivre autre agent :' var: follow_other category: 'Model' ;
	parameter 'Facteur :' var: adjustment_factor category: 'Model' ;
	parameter 'Enable contagion of emotion :' var: apply_propagation category: 'Contagion' ;
	parameter 'Enable decay of emotion :' var: apply_decay category: 'Model' ;
	//parameter 'Ep :' var: apply_propagation category: 'Contagion' ;
	output {
		inspect name:'Agent' type: agent refresh_every: 1;
		inspect name:'Species' type:species refresh_every: 1;			
		
		display building_display refresh_every: 1 {		
			species background aspect : base;
			species obstacle aspect: base ;
			species port aspect: base ;
			species people aspect: base ;
			species fire aspect: image ;		
		}
		monitor name:"Collision:" value: total_collision as int;
		monitor name:"Agents exited:" value: nb_exit as int;
		monitor name:"Means time for exit:" value: total_exit_time/nb_exit;
		monitor name:"Exit time:" value: simulation_time;
		monitor name:"For report:" value: string(total_collision) + " " + total_exit_time/nb_exit;
	}	
}